[contents] [next] [bottom] (1 out of 6)

Chapter 5 - Functions, Threads and Pipes

This chapter describes how to define ScriptX functions and also discusses the ScriptX thread and pipe operators.

Defining Functions

Functions provide a way to group a series of common operations under a single name. As described in Chapter 3, "Working with Objects," there are two kinds of functions in ScriptX: regular functions and generic functions. This section describes how to define regular functions. Generic functions are created as a side effect of defining a method on a class or object. Method definition is described in Chapter 6, "Defining Classes and Objects."

Functions, like all things in ScriptX, are objects (literally, instances of a subclass of AbstractFunction, typically ByteCodeMethod). There are two ways to define functions. A named function definition creates a new variable and immediately assigns the function object to it. An anonymous function is not assigned to a variable, although it can be assigned in a subsequent expression. Anonymous functions are described on page 101.

If you are defining a recursive function, you must use a named function. This is true because a named function is the only kind of implicit or explicit variable declaration in which you can use a name within the initializing expression and have it refer to the variable being defined. In other cases, it refers to an outer scope variable of that name.

Function Syntax Summary

Regular functions with names are defined using the following syntax:

function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \
-> body
The function reserved word, which can be shortened to simply fn, signals that this is the start of a function definition, and fnName is the name of the variable this function object is assigned to. After the function name are the parameters to the function: positionalArgs specify arguments that are required by the function and must be specified in a specific order (position), restArg holds an array of optional arguments given when the function is called, and keywordArgs is a list of keyword arguments. All three types of arguments are optional, although they must be specified in this order.

Finally, body is the expression, often a compound expression, that makes up the body of the function. The expressions within the function body are executed when the function is actually called.

Function Names and Variables

The fnName part of the function definition is the name of the variable that this function object is assigned to once it has been created. Like all variables in ScriptX, if fnName has not been previously declared, it is automatically declared global. You can specify that fnName is to be declared locally by preceding the function keyword with the word local. Note that just as with local variables, you can only define a local function inside a local scope (that is, not at the top level).

local function sortIt theList ->  . . .

Additionally, the variable that this function is assigned to is automatically declared constant in the function definition, which means that you cannot assign anything else to it once the function has been created (except by redefining another function). This is to prevent accidentally overwriting the definition of a function with a simple assignment. If you expect to reuse the global variable name to which you've assigned a function elsewhere in your script, consider using the anonymous function construct instead (or a local variable, if appropriate).

Functions with Positional Arguments

Functions with positional arguments are the simplest form of function, and they look and operate just like functions in other languages. When the function is called, the positional arguments must all be specified and must be in the right order for the function to work.

You can specify any number of positional argument parameters (including none), but they always appear first in the function definition, before any "rest" arguments or keyword arguments.

function square n -> n * n
square 10
100
function beepMe -> print "beep!" 
beepMe()
"beep!"

If a function has no arguments, empty parentheses are
required.

Functions with Keyword Arguments

Functions with keyword arguments provide the most flexibility for the user. You can call such a function with different numbers of arguments, in any order, and some or all of those arguments can be optional. However, calling functions that have been defined to use keyword arguments involves a performance hit because all the keyword arguments must be processed by ScriptX before the function is executed.

To specify that your function takes keyword arguments, use the #key keyword in the function definition, followed by the definitions of the keywords themselves:

function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \
-> body
Keyword arguments are optional in the function definition, but if they are included, they must be specified after both the positional arguments and the optional "rest" argument (described in the next section).

You can specify any number of keyword arguments after the #key reserved word. Keyword arguments look like this:

keyName:[ argName  ] [ ( initialValue )  ]  . . .
where:

If a function with keyword arguments is called with duplicate keywords, the value of the first one in the series is used.

Here are some examples:

-- reportArgs simply prints out its a and b values
function reportArgs #key a: b: -> (print a; print b)
reportArgs a:10 b:20
10
20
reportArgs a:100
100
unsupplied
reportArgs a:10 b:30 a:50
10
30

-- reportArgs2 is the same as reportArgs, but the values
of the
-- keys are assigned to the local variables moo and quack 
-- instead of a and b. 
function reportArgs2 #key a:moo b:quack -> (
print moo; print quack
)
reportArgs2 a:10
10
unsupplied

-- reportArgs3 defines default values for a and b so 
-- neither will appear as unsupplied
function reportArgs3 #key a:(1) b:(2) -> (
	print a; print b
)
reportArgs3() -- use all defaults
1
2
reportArgs3 a:20
20
2

-- reportArgs4 defines default values
-- and assigns them to local variables
function reportArgs4 #key a:moo(1) b:quack(2) -> (
	print moo; print quack
)
reportArgs4()
1
2
reportArgs4 a:6
6
2

Functions with Rest Arguments

ScriptX allows you to define a function that you can call with any number of arguments. For example, you could create a function that adds up all of the numbers passed into it-two numbers, ten numbers, or whatever-the important thing is that the number of arguments is not fixed.

The #rest argument allows you to define a function with a variable number of arguments. In this sense, "rest" means "the rest of the arguments after the positional arguments, except the denoted keyword arguments." The function collects those "rest" values into an array, and in the body of the function you can access elements in that array.

Unlike positional arguments, rest arguments are not required-when you call the function you can supply no rest arguments, or as many as you need to.

Notice in the following function definition that #rest takes a single argument restArg. The combination #rest restArg comes after any positional arguments and before any keyword arguments. By convention, the argument args is typically used for restArg (though you could name it whatever you want), so what you often see in examples is the combination #rest args.

function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \
-> body
This is an example of a function that takes no arguments other than its rest arguments:

-- addEmUp takes a variable number of arguments, sums them all
-- and returns the sum
function addEmUp #rest args -> (
	local sumArgs := 0
	for i in args do sumArgs := sumArgs + i
)
addEmUp 1 2 3 4 5
15
addEmUp 3 4
7

In place of using args in the previous example, we could have used numList, to denote the list of numbers.

This example defines a function that takes a single required argument and a variable number of rest arguments (each argument happens to itself be an array):

-- joinArray takes a single required array and a variable number 
-- of other arrays. It builds a single array of all the 
-- elements in all its arguments
function joinArray array1 #rest otherArrays -> (
	for i in otherArrays do addMany array1 i
	return array1
)
joinArray #(1,2,3) #(4,5,6)
#(1, 2, 3, 4, 5, 6)
joinArray #() #(@angora,@persian) #(@egyptianmau) #(@housecat)
#(@angora, @persian, @egyptianmau, @housecat)

Combining Rest and Keyword Arguments

If your function definition uses both the #rest and #key reserved words to define both rest and keyword arguments, the non-positional arguments can only be in the form of keyword-value pairs (keyword:value). You cannot mix arbitrary numbers of #rest arguments and keyword arguments.

When a function with both rest and keyword arguments is called, the keywords and values supplied to the function are stored into the rest argument in sequence (key, value, key, value, and so on) with the keywords appearing as NameClass objects.

function showRest #rest args #key a: b: c:(100) ->
	print args debug

showRest a:10 b:20 c:30
#(@a, 10, @b, 20, @c, 30)

showRest a:10
#(@a, 10)

showRest()
#()

Note that the compiler translates a keyword argument into a pair of conventional arguments. The following function calls are equivalent:

grok foo:10 moof:20
grok @foo 10 @moof 20

Function Return Values

You can specify a function's return value using the return block control expression:

return expression
When a return expression is encountered in the body of a function definition, the function is immediately exited with the value specified by expression.

Functions that do not specify a specific return value return the value of the last expression evaluated while executing the function.

function factorial n -> (
	if n <= 0 then return 1 
	else return (n * (factorial (n - 1)))
)
-- now, try a few test values
factorial 0
1
factorial 4
24
function sumAndPrint #rest args -> (
	local mySum := 0
	for i in args do mySum := mySum + i
	format debug "The sum is: %*\n" mySum @normal
	return mySum
)

sumAndPrint 10 20 34 45
The sum is: 109
109


This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.

Copyright 1996 Apple Computer, Inc. All Rights Reserved.